﻿using System;
using System.EnterpriseServices;
using System.IO;
using System.Reflection;
using NHConfiguration;
using NHibernate;
using NHibernate.Cfg;
using Configuration = NHibernate.Cfg.Configuration;

namespace NHDataAccess
{
    [Transaction(TransactionOption.Supported)]
    public sealed class NhibernateHelper : ServicedComponent
    {
        private const string CurrentSessionKey = "nhibernate.current_session";
        public static readonly ISessionFactory SessionFactory;
        public static readonly NhTypeApplication TypeApplication;

        static NhibernateHelper()
        {
            try
            {
                log4net.Config.XmlConfigurator.Configure();
                var cfg = new Configuration();
                var configurationSection = NhibernateConfigurationSection.CurrentConfiguration;
                cfg.Configure(configurationSection.HibernateFile);
                var path = new DirectoryInfo(configurationSection.MappingPath);
                TypeApplication = configurationSection.NhTypeApplication;
                cfg.AddDirectory(path);
                SessionFactory = cfg.BuildSessionFactory();
            }
            catch
            {
                throw new Exception("Falló Inicialización de Nhibernate");
            }
        }

        

        public static ISession OpenSession()
        {
            var session = SessionFactory.OpenSession();
            TryEnlistDistributedTransaction(session);
            return session;
        }

        public static ISession GetCurrentSession()
        {
            ISession currentSession = null ;
            switch (TypeApplication)
            {
                case NhTypeApplication.Web:
                    var context = System.Web.HttpContext.Current;
                    currentSession = (ISession)(context.Items[CurrentSessionKey]);
                    if (currentSession == null)
                    {
                        currentSession = SessionFactory.OpenSession();
                        context.Items[CurrentSessionKey] = currentSession;
                    }
                    return currentSession;
                case NhTypeApplication.WebService:
                    currentSession = NhibernateContext.Current().Session ?? SessionFactory.OpenSession();
                    return currentSession;
                case NhTypeApplication.Test:
                    currentSession = SessionFactory.OpenSession();
                    return currentSession;
            }
            return currentSession;
        }

        public static void CloseSession()
        {
            switch (TypeApplication)
            {
                case NhTypeApplication.Web:
                    var context = System.Web.HttpContext.Current;
                    var currentSession = (ISession)(context.Items[CurrentSessionKey]);
                    if (currentSession == null)
                    {
                        return;
                    }
                    currentSession.Close();
                    context.Items.Remove(CurrentSessionKey);
                    break;
                case NhTypeApplication.WebService:
                    var operation = NhibernateContext.Current();
                    if (operation == null)
                        return;
                    operation.Session.Dispose();
                    break;
            }
            
        }

        public static void CloseSessionFactory()
        {
            if(!(SessionFactory == null))
            {
                SessionFactory.Close();
            }
        }

        public static void BeginTransaction()
        {
            try
            {
                //if (GetCurrentSession().Transaction.IsActive)
                //{
                    var serviceConfig = new ServiceConfig { Transaction = TransactionOption.RequiresNew };
                    ServiceDomain.Enter(serviceConfig);
                    GetCurrentSession().BeginTransaction();
                //}
            }
            catch (HibernateException exception)
            {
                throw new HibernateException("No se puedo inicializar Transacción");
            }
            
        }

        public static void EndTransaction()
        {
            try
            {
                ContextUtil.SetComplete();
                ServiceDomain.Leave();
                GetCurrentSession().Transaction.Commit();
            }
            catch (HibernateException exception)
            {
                throw new HibernateException("No se pudo finalizar Transacción");
            }
            
        }

        public static void RollBackTransaction()
        {
            try
            {
                ContextUtil.SetAbort();
                GetCurrentSession().Transaction.Rollback();
            }
            catch (HibernateException hibernateException)
            {
                throw new HibernateConfigException("No se pudo dar vueltra atrás con la Transacción");
            }
        }
        private static void TryEnlistDistributedTransaction(ISession session)
        {
            if (ContextUtil.IsInTransaction)
            {
                var conn = session.Connection;
                var mi = conn.GetType().GetMethod("EnlistDistributedTransaction",BindingFlags.Public | BindingFlags.Instance);
                if (mi != null)
                    mi.Invoke(conn,new object[] {ContextUtil.Transaction });
            }
        }
    }
}